package utils

import (
	"context"
	"fmt"
	"log"
	"miaoyu/internal/model"
	"miaoyu/internal/model/entity"
	"miaoyu/internal/service"
	"strings"

	casbin "github.com/dobyte/gf-casbin"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/os/gctx"
	"github.com/gogf/gf/v2/util/gconv"
)

type sAccess struct {
	Enforce *casbin.Enforcer
}

func NewAccess() *sAccess {
	var accessS = sAccess{}
	var ctx = gctx.New()
	config, _ := g.Cfg().Get(ctx, "casbin")
	var options casbin.Options

	db := g.DB("default")

	confErr := gconv.Scan(config, &options)
	if confErr != nil {
		fmt.Println("init casbin config error")
	}

	options.DB = db
	e, err := casbin.NewEnforcer(&options)
	if err != nil {
		log.Fatalf("Casbin init failure:%s \n", err.Error())
	}

	accessS.Enforce = e
	return &accessS
}

func init() {
	service.RegisterAccess(NewAccess())
}
func (s *sAccess) MiddleWareFunc(r *ghttp.Request) {
	ctx := r.GetCtx()
	user := service.Session().GetUser(ctx)
	//userId := gconv.Int(service.Auth().GetIdentity(ctx))
	path := r.RequestURI
	if strings.Index(path, "?") <= 0 {
		path += "?"
	}
	isPass, err := s.CheckAccess(ctx, user, path)
	if err != nil {
		g.Log().Error(ctx, "授权验证失败", err)
		service.JsonResp().CommonResp(r).SetErrorJsonExit("验证失败！")
	}
	if isPass {
		r.Middleware.Next()
	} else {
		//response1.UniResp(r, "未授权请求！", "")
		service.JsonResp().CommonResp(r).SetErrorJsonExit("验证失败！")
	}

}

// CheckAccess 检查是否能通过
func (s *sAccess) CheckAccess(ctx context.Context, user *model.ContextUser, path string) (isPass bool, err error) {
	// check role_1 policy
	var userStr string

	if user.IsAdmin {
		isPass = true
		return
	}

	userStr = "u_" + gconv.String(user.Id)

	ok, reason, checkErr := s.Enforce.EnforceEx(userStr, path)
	if checkErr != nil {
		err = checkErr
		return
	}
	fmt.Println(ok, reason) // true [admin data1 read]
	isPass = ok
	return
}
func (s *sAccess) AddPolicy(role int64, menu entity.MyMenu) (bool, error) {
	roleStr := "r_" + gconv.String(role)
	var path string
	if menu.PathParam == "" {
		path = menu.Path + "?*"
	} else {
		path = menu.Path + "?" + menu.PathParam + "*"
	}

	return s.Enforce.AddPolicy(roleStr, path)
}

// AddPolicies  exp [{id,path,action}]
func (s *sAccess) AddPolicies(role int64, menuList []entity.MyMenu) (bool, error) {
	listNum := len(menuList)
	menuArr := make([][]string, listNum)

	for key, val := range menuList {
		if val.PathParam == "" {
			//menuArr[key] = val.Path + "\?*"
			menuArr[key] = []string{"r_" + gconv.String(role), val.Path + "?*"}
		} else {
			//menuArr[key] =val.Path + "\?" + val.PathParam
			menuArr[key] = []string{"r_" + gconv.String(role), val.Path + "?" + val.PathParam + "*"}
		}
	}

	//ruleList := make([][]string, listNum)
	//for key, val := range menuArr {
	//	ruleList[key] = []string{"r_" + gconv.String(role), val}
	//
	//}

	return s.Enforce.AddPolicies(menuArr)
}

// AddGroupingPolicy  userId roleId
func (s *sAccess) AddGroupingPolicy(user int64, role int64) (bool, error) {
	userStr := "u_" + gconv.String(user)
	roleStr := "r_" + gconv.String(role)
	return s.Enforce.AddGroupingPolicy(userStr, roleStr)
}

// AddGroupingPolicies  {userId,roleId}  exp:{1,3}
func (s *sAccess) AddGroupingPolicies(user int64, ruleList []int64) (bool, error) {
	listLength := len(ruleList)
	rules := make([][]string, listLength)
	for key, val := range ruleList {
		rules[key] = []string{"u_" + gconv.String(user), "r_" + gconv.String(val)}

	}
	return s.Enforce.AddGroupingPolicies(rules)
}

// RemoveAccessList 更新删除指定数据
func (s *sAccess) RemoveAccessList(list [][]string) (isRemove bool) {

	return
}

func (s *sAccess) DeleteRole(role int64) (res bool, err error) {
	res, err = s.Enforce.DeletePermissionsForUser("r_" + gconv.String(role))

	return
}
func (s *sAccess) DeleteUserRoles(user int64) (res bool, err error) {
	res, err = s.Enforce.DeleteRolesForUser("u_" + gconv.String(user))

	return
}

// UpdateRolePolicy 更新casbin策略库 角色-菜单策略库
func (s *sAccess) UpdateRolePolicy(ctx context.Context, role int64, menus []int64) (res bool, err error) {
	//更新casbin策略库
	//只获取可用菜单
	menuList, menuErr := service.Menu().GetAllEnableList(ctx, g.MapStrAny{"id IN (?)": menus})
	if menuErr != nil {
		err = menuErr
		return
	}
	//1.删除原角色策略库，更新到最新策略库
	_, err = s.DeleteRole(role)
	if err != nil {
		return
	}
	//2.添加角色新策略
	//var ruleList []string
	res, err = s.AddPolicies(role, menuList)
	if err != nil {
		return
	}
	s.Enforce.LoadPolicy()
	return
}

// UpdateUserRole 更新casbin策略库 用户-角色策略库
func (s *sAccess) UpdateUserRole(ctx context.Context, user int64, roleIdList []int64) (res bool, err error) {
	//更新casbin策略库
	//只获取可用菜单
	roleList, roleErr := service.Role().GetAllEnableList(ctx, g.MapStrAny{"id IN (?)": roleIdList})
	if roleErr != nil {
		err = roleErr
		return
	}
	//1.删除原角色策略库，更新到最新策略库
	_, err = s.DeleteUserRoles(user)
	if err != nil {
		return
	}
	//2.添加角色新策略
	//var ruleList []string
	ruleList := make([]int64, len(roleList))
	for k, v := range roleList {
		ruleList[k] = v.Id
	}
	res, err = s.AddGroupingPolicies(user, ruleList)
	if err != nil {
		return
	}
	s.Enforce.LoadPolicy()
	return
}
